package com.gksc.video.manager;

import cn.hutool.core.date.DateUtil;
import com.gksc.video.config.GkzfWebProperties;
import com.gksc.video.config.PushConfig;
import com.gksc.video.constant.BusinessConstant;
import com.gksc.video.constant.CacheConstant;
import com.gksc.video.data.DeviceInfo;
import com.gksc.video.data.OpResInfo;
import com.gksc.video.enums.DeviceFirm;
import com.gksc.video.enums.PushMethod;
import com.gksc.video.enums.StreamType;
import com.gksc.video.utils.Tool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @className: VideoManager
 * @description: 视频服务管理
 * @author: Liao.yx
 * @email: 1229053515@qq.com
 * @create: 2021年05月15日 11时17分
 * @copyRight: 2020 liaoyongxiang All rights reserved.
 **/
@Slf4j
@Component
public class VideoManager {
    @Autowired
    private GkzfWebProperties properties;
    @Qualifier("taskExecutor")
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;

    /**
     * 打开视频
     *
     * @param device 设备基础信息
     * @param type   打开类型
     * @return
     */
    public OpResInfo<DeviceInfo> openVideo(DeviceInfo device, int type) {
        OpResInfo<DeviceInfo> opRes = null;
        switch (type) {
            case BusinessConstant.OPEN_STREAM_REAL:
                opRes = openByStream(device, false);
                break;
            case BusinessConstant.OPEN_DEVICE_REAL:
                //opRes = openByDevice(device, false);
                opRes = new OpResInfo<>("预览实时视频请组装摄像头rtsp地址", device, false);
                break;
            case BusinessConstant.OPEN_STREAM_HIS:
                opRes = openByStream(device, true);
                break;
            case BusinessConstant.OPEN_DEVICE_HIS:
                opRes = openByDevice(device, true);
                break;
        }
        return opRes;
    }

    /**
     * 维续视频存活时间
     *
     * @param tokens 请求参数
     * @return
     */
    public OpResInfo<Boolean> keepAlive(List<String> tokens) {
        if (ObjectUtils.isEmpty(tokens)) {
            return new OpResInfo<>("数据列表不能为空", false, false);
        }
        for (String token : tokens) {
            DeviceInfo device = CacheConstant.STREATMAP.get(token);
            if (device != null) {
                device.setOpenTime(new Date());
                CacheConstant.STREATMAP.put(token, device);
            }
        }
        return new OpResInfo<>("", true, true);
    }


    /**
     * 关闭视频
     *
     * @param tokens
     * @return
     */
    public OpResInfo<List<DeviceInfo>> closeVideo(List<String> tokens) {
        if (ObjectUtils.isEmpty(tokens)) {
            return new OpResInfo<>("数据列表不能为空", null, false);
        }
        List<DeviceInfo> resList = new ArrayList<>();
        for (String token : tokens) {
            DeviceInfo device = CacheConstant.STREATMAP.get(token);
            BasePushThread task = CacheConstant.JOBMAP.get(token);
            if (!ObjectUtils.isEmpty(device) && !ObjectUtils.isEmpty(device)) {
                int count = device.getCount();
                if (count == 1) {
                    task.cancel();
                    device.setCount(0);
                    CacheConstant.STREATMAP.put(token, device);
                } else {
                    device.setCount(count - 1);
                    CacheConstant.STREATMAP.put(token, device);
                }
                resList.add(device);
                log.debug("关闭视频device{}", device.toString());
            }
        }
        return new OpResInfo<>("", resList, true);
    }

    /**
     * 通过设备打开视频
     *
     * @param device  设备信息
     * @param history 是否为回放视频
     * @return
     */
    private OpResInfo<DeviceInfo> openByDevice(DeviceInfo device, boolean history) {
        OpResInfo<Boolean> validFlag = checkOpenVideoParam(device, history);
        if (validFlag.getData()) {
            boolean openFlag = true;//新建推流线程任务
            String existKey = "";
            if (!ObjectUtils.isEmpty(CacheConstant.STREATMAP)) {
                for (Map.Entry<String, DeviceInfo> item : CacheConstant.STREATMAP.entrySet()) {
                    DeviceInfo cacheInfo = item.getValue();
                    if (Tool.checkDevicePush(device, cacheInfo, history)) {
                        openFlag = false;
                        existKey = item.getKey();
                        break;
                    }
                }
            }
            if (openFlag) {
                boolean flag = pushStream(device, history, PushMethod.SDK.getType());
                return new OpResInfo<>(flag ? "" : "打开视频流建立通信失败", device, flag);
            } else {
                DeviceInfo cacheInfo = CacheConstant.STREATMAP.get(existKey);
                cacheInfo.setCount(cacheInfo.getCount() + 1);
                cacheInfo.setOpenTime(new Date());
                CacheConstant.STREATMAP.put(existKey, cacheInfo);
                return new OpResInfo<>("", cacheInfo, true);
            }
        } else {
            return new OpResInfo<>(validFlag.getErrorDesc(), null, false);
        }
    }

    /**
     * 通过流打开视频
     *
     * @param device  设备信息
     * @param history 是否为回放视频
     * @return
     */
    private OpResInfo<DeviceInfo> openByStream(DeviceInfo device, boolean history) {
        String rtspUrl = device.getRtspUrl();
        if (ObjectUtils.isEmpty(device.getRtspUrl())) {
            OpResInfo<String> openRes = getOpenRtspUrl(device, history);
            if (openRes.isOpRes()) {
                rtspUrl = openRes.getData();
            } else {
                return new OpResInfo<>(openRes.getData(), null, false);
            }
        }
        if (history) {
            rtspUrl = getOpenRtspUrl(device, true).getData();
        }
        boolean openFlag = true;//新建推流线程任务
        String existKey = "";
        if (!ObjectUtils.isEmpty(CacheConstant.STREATMAP)) {
            for (Map.Entry<String, DeviceInfo> item : CacheConstant.STREATMAP.entrySet()) {
                DeviceInfo cacheInfo = item.getValue();
                if (rtspUrl.equals(cacheInfo.getDealUrl())) {
                    openFlag = false;
                    existKey = item.getKey();
                    break;
                }
            }
        }
        if (openFlag) {
            device.setDealUrl(rtspUrl);
            boolean flag = pushStream(device, history, PushMethod.RTSP.getType());
            return new OpResInfo<>(flag ? "" : "打开视频流建立通信失败", device, flag);
        } else {
            DeviceInfo cacheInfo = CacheConstant.STREATMAP.get(existKey);
            cacheInfo.setCount(cacheInfo.getCount() + 1);
            cacheInfo.setOpenTime(new Date());
            CacheConstant.STREATMAP.put(existKey, cacheInfo);
            return new OpResInfo<>("", cacheInfo, true);
        }
    }

    /**
     * rtsp转码rtmp播放
     *
     * @param device  设备信息
     * @param history 是否为回放视频
     * @param method  推流方法
     * @return
     */
    private boolean pushStream(DeviceInfo device, boolean history, int method) {
        StringBuilder rtmpUrl = new StringBuilder();
        PushConfig config = properties.getPushCfg();
        String token = Tool.getNextId();
        if (history) {
            if(properties.isInnerVideoServerEnable()){
                rtmpUrl.append("rtmp://").append(config.getPushHost()).append(":").append(config.getPushPort()).append("/history/stream1");
            }else{
                rtmpUrl.append("rtmp://").append(config.getPushHost()).append(":").append(config.getPushPort()).append("/history/").append(token);
            }
        } else {
            if(properties.isInnerVideoServerEnable()){
                rtmpUrl.append("rtmp://").append(config.getPushHost()).append(":").append(config.getPushPort()).append("/live/stream1");
            }else{
                rtmpUrl.append("rtmp://").append(config.getPushHost()).append(":").append(config.getPushPort()).append("/live/").append(token);
            }
        }
        device.setRtmpUrl(rtmpUrl.toString());
        device.setToken(token);
        device.setCount(1);
        device.setOpenTime(new Date());
        Socket rtspSocket = new Socket();
        Socket rtmpSocket = new Socket();

        try {
            rtspSocket.connect(new InetSocketAddress(device.getDevIp(), 554), 1000);
        } catch (IOException e) {
            log.error("与拉流IP：{},端口：554,建立TCP连接失败！", device.getDevIp(), e);
            e.printStackTrace();
            return false;
        }
        try {
            rtmpSocket.connect(new InetSocketAddress(config.getPushHost(), config.getPushPort()), 1000);
        } catch (IOException e) {
            log.error("与推流IP：{},端口：{},建立TCP连接失败！", config.getPushHost(), config.getPushPort(), e);
            e.printStackTrace();
            return false;
        }
        if (method == PushMethod.SDK.getType()) {
            DevicePushThread pushTask = new DevicePushThread(device, config);
            taskExecutor.submit(pushTask);
        } else if (method == PushMethod.RTSP.getType()) {
            StreamPushThread pushTask = new StreamPushThread(device, config);
            taskExecutor.submit(pushTask);
        }
        return true;
    }

    /**
     * 检查打开视频参数
     *
     * @param param   请求参数
     * @param history 是否回放
     * @return
     */
    private OpResInfo<Boolean> checkOpenVideoParam(DeviceInfo param, boolean history) {
        if (!Tool.checkDeviceInfo(param.getDevIp(), param.getDevPort() + "", param.getUsername(), param.getPassword())) {
            return new OpResInfo<>("参数不合法", false, false);
        }
        if (!Tool.checkIpAddress(param.getDevIp())) {
            return new OpResInfo<>("IP地址不合法", false, false);
        }
        if (ObjectUtils.isEmpty(param.getDevFirm())) {
            return new OpResInfo<>("设备厂商信息不能为空", false, false);
        }
        if (history) {
            if (ObjectUtils.isEmpty(param.getStartTime()) || ObjectUtils.isEmpty(param.getEndTime()) ||
                    param.getStartTime().after(param.getEndTime())) {
                return new OpResInfo<>("回放时间不合法", false, false);
            }
        }

        return new OpResInfo<>("", true, true);
    }

    /**
     * 获取打开视频的rtsp地址
     *
     * @param device  设备信息
     * @param history 是否回放
     * @return
     */
    private OpResInfo<String> getOpenRtspUrl(DeviceInfo device, boolean history) {
        OpResInfo<Boolean> validFlag = checkOpenVideoParam(device, history);
        if (!validFlag.getData()) {
            log.error("设备参数信息{}不合法", device.toString());
            return new OpResInfo<>("参数信息不合法", null, false);
        }
        StringBuilder sb = new StringBuilder();
        if (DeviceFirm.Hik.getCode().equals(device.getDevFirm())) {
            if (history) {
                if (StringUtils.isEmpty(device.getRtspUrl())) {
                    sb.append("rtsp://").append(device.getUsername()).append(":").append(device.getPassword()).append("@").append(device.getDevIp());
                    sb.append(":").append(device.getDevPort()).append("/Streaming/tracks/").append(device.getChannel() - 32).append("01");
                } else {
                    sb.append(device.getRtspUrl());
                }
                if (!ObjectUtils.isEmpty(device.getStartTime()) && !ObjectUtils.isEmpty(device.getEndTime())) {
                    sb.append("?starttime=").append(DateUtil.format(device.getStartTime(), BusinessConstant.HK_DATE_SDF)).append("t");
                    sb.append(DateUtil.format(device.getStartTime(), BusinessConstant.HK_TIME_SDF)).append("z").append("&endtime=");
                    sb.append(DateUtil.format(device.getEndTime(), BusinessConstant.HK_DATE_SDF)).append("t");
                    sb.append(DateUtil.format(device.getEndTime(), BusinessConstant.HK_TIME_SDF)).append("z");
                }
            } else {
                sb.append("rtsp://").append(device.getUsername()).append(":").append(device.getPassword()).append("@").append(device.getDevIp());
                sb.append(":").append(device.getDevPort()).append("/h264/ch").append(device.getChannel()).append("/");
                sb.append(StreamType.getStreamTypeCode(device.getStream())).append("/av_stream");
            }
        } else if (DeviceFirm.Dahua.getCode().equals(device.getDevFirm())) {
            if (history) {
                if (StringUtils.isEmpty(device.getRtspUrl())) {
                    sb.append("rtsp://").append(device.getUsername()).append(":").append(device.getPassword()).append("@").append(device.getDevIp()).append(":");
                    sb.append(device.getDevPort()).append("/cam/playback?channel=").append(device.getChannel()).append("&subtype=").append(device.getStream());
                } else {
                    sb.append(device.getRtspUrl());
                }
                if (!ObjectUtils.isEmpty(device.getStartTime()) && !ObjectUtils.isEmpty(device.getEndTime())) {
                    sb.append("&starttime=").append(DateUtil.format(device.getStartTime(), BusinessConstant.DH_DATETIME_SDF));
                    sb.append("&endtime").append(DateUtil.format(device.getEndTime(), BusinessConstant.DH_DATETIME_SDF));
                }
            } else {
                sb.append("rtsp://").append(device.getUsername()).append(":").append(device.getPassword()).append("@").append(device.getDevIp());
                sb.append(":").append(device.getDevPort()).append("/cam/realmonitor?channel=").append(device.getChannel());
                sb.append("&subtype=").append(device.getStream());
            }
        } else {
            log.error("该型号的厂商设备{}暂未实现对接", device.toString());
            return new OpResInfo<>("该型号的厂商设备暂未实现对接", null, false);
        }
        return new OpResInfo<>("操作成功", sb.toString(), true);
    }
}
